
/**
 * 递归实现深拷贝
 * 对于复杂对象或需要处理特殊值（如函数、undefined、symbol等），可以手动实现深拷贝。
 * @param {*} obj 
 */

function deepcode(obj, hash = new WeakMap()) {

  if (obj === null) {
    return false
  }
  if (typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj)
  if (obj instanceof RegExp) return new RegExp(obj);
  // 处理函数对象
  if (typeof data === 'function') {
    return new Function('return ' + data.toString())()
  }

  if (hash.has(obj)) return hash.get(obj); // 处理循环引用

  // 处理Map对象
  if (data instanceof Map) {
    const result = new Map()
    hash.set(data, result)
    data.forEach((val, key) => {
      // 注意：map中的值为object的话也得深拷贝
      if (typeof val === 'object') {
        result.set(key, deepcode(val,hash))
      } else {
        result.set(key, val)
      }
    })
    return result
  }

  // 处理Set对象
  if (data instanceof Set) {
    const result = new Set()
    hash.set(data, result)
    data.forEach(val => {
      // 注意：set中的值为object的话也得深拷贝
      if (typeof val === 'object') {
        result.add(deepcode(val,hash))
      } else {
        result.add(val)
      }
    })
    return result
  }

  let cloneObj = Array.isArray(obj) ? [] : {};

  hash.set(obj, cloneObj); // 存储原始对象和克隆对象的对应关系

  // 遍历数组 or 对象(string类型的属性)
  // 为for...in遍历的对象身上可枚举的string类型属性

  // 拿到对象身上所有的属性，返回一个数组(包含string类型的属性、不可枚举的属性、symbol类型的深呼吸)
  const keys = Reflect.ownKeys(obj);
  for (let key of keys) {
    cloneObj[key] = deepcode(obj[key], hash)

  }

  //  Symbol 类型属性
  // const symbols = Object.getOwnPropertySymbols(obj);
  // for (const symbolKey of symbols) {
  //   cloneObj[symbolKey] = deepClone(obj[symbolKey]);
  // }

  return cloneObj;

}

/**
 * structuredClone
 * 描述：Window 接口的 structuredClone() 方法使用结构化克隆算法将给定的值进行深拷贝。
 * 该方法还支持把原值中的可转移对象转移（而不是拷贝）到新对象上。可转移对象与原始对象分离并附加到新对象；它们将无法在原始对象中被访问。
 * https://developer.mozilla.org/zh-CN/docs/Web/API/Window/structuredClone
 */

function deepcopy2(obj) {
  return window.structuredClone(obj)
}

let obj = {
  a: 2,
  c: 3
}
let c = deepcopy2(obj);
c.c = 3
console.log('c', c);
console.log('d', obj);




/**
 * for...in 
 * 语句循环一个指定的变量来循环一个对象所有可枚举的属性。JavaScript 会为每一个不同的属性执行指定的语句。
 * fon in 性能很差：迭代当前对象中中所有可枚举的属性的【 私有属性大部分是可以枚举的，公有属性{出现在所属类的原型上的，} 也有部分是 可枚举的 】 查找机制上一定会搞到原型链上去。
 * 遍历顺序，会以数字优先
 * 无法遍历symbol 类型的变量
 * 不建议用于循环数组
 */


/**
 * for...of 
 * 语句在可迭代对象（包括Array、Map、Set、arguments 等等）上创建了一个循环，对值的每一个独特属性调用一次迭代。
 * for of 可以遍历 数组，因为数组有迭代器，但是不能遍历对象，对象没有。
 * 
 */


/**
 * obj.hasOwnProperty(key)
 * 描述：hasOwnProperty() 方法返回一个布尔值，表示对象自有属性（而不是继承来的属性）中是否具有指定的属性。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
 */


/**
 * Object.hasOwn(obj,key)
 * 描述：如果指定的对象自身有指定的属性，则静态方法 Object.hasOwn() 返回 true。如果属性是继承的或者不存在，该方法返回 false。
 * Object.hasOwn 是ES2022（ECMAScript 2022）中引入的一个静态方法，它与 hasOwnProperty 类似，但提供了一种更现代、更简洁的方式来检查属性是否存在。这个方法的好处是它不依赖于对象的原型，可以直接作为一个静态方法来使用，不需要通过对象实例来调用。
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
 */


/**
 * Reflect.ownKeys()
 * 
 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys
 */